﻿(function () {
    "use strict";

    var RPC_HOOK_FACILITY = 500;

    function makeHResult(sev, fac, code) {
        return (sev << 31) | (fac << 16) | (code);
    };

    function makeHookError(code) {
        return makeHResult(1, RPC_HOOK_FACILITY, code);
    };

    var E_FAIL = makeHResult(1, 0, 0x4005);
    var E_INVALIDARG = makeHResult(1, 7, 0x0057);

    var ARCHIVE_FIELD_NOT_FOUND = makeHookError(0);
    var STRING_BUFFER_TOO_SMALL = makeHookError(1);
    var ARCHIVE_VALUE_IS_NULL = makeHookError(2);

    var TEST_EXCEPTION_1 = makeHookError(600);
    var TEST_EXCEPTION_2 = makeHookError(601);
    var TEST_EXCEPTION_3 = makeHookError(602);

    var rpcServer = null;
    var rpcServer2 = null;
    var rpcServer3 = null;
    var rpcServerWithQueueSize = null;

    var testHookOrderCounter = 0;
    
    // There is no good way to suspend execution of a function
    // that prevents it from returning. As such for the one
    // function that uses it, this will spin until the time has passed.
    function sleep(milliseconds) {
        var start = new Date().getTime();
        for (var i = 0; i < Number.MAX_VALUE - 1; i++) {
            if ((new Date().getTime() - start) > milliseconds) {
                break;
            }
        }
    };

    function reverseTypedArray(array) {
        var tmp;
        for (var i = 0; i < Math.floor(array.length / 2) ; ++i) {
            tmp = array[i];
            array[i] = array[array.length - 1 - i];
            array[array.length - 1 - i] = tmp;
        }
    };

    function initializeOutgoingPlayer() {
        var p = new RPC.CustomTypes.Player();
        p.name = "Ragnor The Terrible";
        p.health = 0.34245;
        p.spells = ["Fireball", "Freeze", "Hurricane", "Force"];
        p.weapons = [new RPC.CustomTypes.Weapon(), new RPC.CustomTypes.Weapon()];
        p.weapons[0].damage = 13454;
        p.weapons[0].modifier = -0.6;
        p.weapons[1].damage = 923;
        p.weapons[1].modifier = 2.4534;
        p.stature.height = 82;
        p.stature.weight = 63;
        return p;
    };

    var ExpectedNumSpells = 4;
    var ExpectedNumWeapons = 2;

    function verifyIncomingPlayerData(playerData) {
        var hr = E_FAIL;

        if (!(Math.abs(playerData.health - 76.375) < 0.0001)) {
            return E_INVALIDARG;
        }
        if (playerData.name != "Brian The Awesome") {
            return E_INVALIDARG;
        }
        if (playerData.spells.length != ExpectedNumSpells) {
            return E_INVALIDARG;
        }

        if (playerData.spells[0] != "Heal") {
            return E_INVALIDARG;
        }
        if (playerData.spells[1] != "Rage") {
            return E_INVALIDARG;
        }
        if (playerData.spells[2] != "Morph") {
            return E_INVALIDARG;
        }
        if (playerData.spells[3] != "Haste") {
            return E_INVALIDARG;
        }

        if (playerData.weapons.length != ExpectedNumWeapons) {
            return E_INVALIDARG;
        }

        if (playerData.weapons[0].damage != 1411518807) {
            return E_INVALIDARG;
        }
        if (!(Math.abs(playerData.weapons[0].modifier - 64848978978.8971) < 0.0001)) {
            return E_INVALIDARG;
        }

        if (playerData.weapons[1].damage != 576460752) {
            return E_INVALIDARG;
        }
        if (!(Math.abs(playerData.weapons[1].modifier - -1796456897.68642) < 0.0001)) {
            return E_INVALIDARG;
        }

        if (playerData.stature.height != 73) {
            return E_INVALIDARG;
        }
        if (playerData.stature.weight != 189) {
            return E_INVALIDARG;
        }

        return 0;
    };


    function addArchiveToArchive(input, output) {
        var outRpcArchive = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();

        try {
            var inRpcArchive = input.getNamedArchiveValue("param01");
        } catch (e) {
            return e.number;
        }
        
        var result = inputOutput(inRpcArchive, outRpcArchive);

        if (result < 0) {
            return result;
        }

        try {
            output.setNamedArchiveValue("resultLevel1", outRpcArchive);
        } catch (e) {
            return e.number;
        }
    };

    function addArchiveToArchiveWithIntColl(input, output) {
        try {
            var inRpcArchive = input.getNamedArchiveValue("param01");

            var outRpcArchive = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();

            inputOutputIntCollection(inRpcArchive, outRpcArchive);

            output.setNamedArchiveValue("resultLevel1", outRpcArchive);
        } catch (e) {
            return e.number;
        }
    };

    function addArchiveToArchiveWithStringColl(input, output) {
        try {
            var inRpcArchive1 = input.getNamedArchiveValue("param01");
            var inRpcArchive2 = inRpcArchive1.getNamedArchiveValue("param01");

            var outRpcArchive1 = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();
            var outRpcArchive2 = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();

            inputOutputStringCollection(inRpcArchive2, outRpcArchive1);

            outRpcArchive2.setNamedArchiveValue("resultLevel2", outRpcArchive1);

            output.setNamedArchiveValue("resultLevel1", outRpcArchive2);
        } catch (e) {
            return e.number;
        }
    };

    function addArchiveToArchiveWithInt64Coll(input, output) {
        try {
            var inRpcArchive = input.getNamedArchiveValue("param01");

            var outRpcArchive = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();

            inputOutputInt64Collection(inRpcArchive, outRpcArchive);

            output.setNamedArchiveValue("resultLevel1", outRpcArchive);
        } catch (e) {
            return e.number;
        }
    };

    function callProcessPendingRequestsOnceOnServer3(input, output) {
        rpcServer3.processPendingRequestsAsync();
        // Because this is asyncronous and as there is no way to
        // wait on an async event in javascript this hook can
        // return prematurely. As such this will just wait a little bit to finish.
        sleep(500);
    };

    function echoStrings(input, output) {
        try {
            var numStrings = input.getNamedWordValue("numStrings");
            var stringArray = new Array(numStrings);

            for (var i = 0; i < numStrings; ++i) {

                var fieldName = "input" + ("00" + i).slice(-3);

                var namedValue = input.getNamedStringValue(fieldName);
                stringArray[i] = (namedValue);
            }

            output.setNamedWordValue("numStrings", numStrings);

            for (var i = 0; i < numStrings; ++i) {

                var fieldName = "output" + ("00" + i).slice(-3);

                var outgoingString = "Echo:" + stringArray[i];

                output.setNamedStringValue(fieldName, outgoingString);
            }
        } catch (e) {
            return e.number;
        }
    };

    function getHookOrderCounter(input, output) {
        try {
            output.setNamedShortValue("result", testHookOrderCounter);
        } catch (e) {
            return e.number;
        }
    };

    function hookOrderIncrementNoOutput(input, output) {
        testHookOrderCounter = testHookOrderCounter + 1;
    };

    function hookTimesOut(input, output) {
        sleep(31000);
    };

    function inputNoOutput(input, output) {
        try {
            var inputParam1 = input.getNamedFloatValue("param01");

            if (Math.abs(inputParam1 - 3.14156) > 0.0001) {
                return E_INVALIDARG
            }
        } catch (e) {
            return e.number;
        }
    };

    function inputOutput(input, output) {
        try {
            var inputParam1 = input.getNamedIntValue("param01");
            var inputParam2 = input.getNamedIntValue("param02");

            if (inputParam1 != 211 || inputParam2 != 87) {
                return E_INVALIDARG;
            }

            output.setNamedIntValue("result", inputParam1 + inputParam2);
        } catch (e) {
            return e.number;
        }
    };

    function inputOutputArchiveCollectionToArchive(input, output) {
        try {
            var inputParam01 = input.getNamedArchiveCollection("param01");
            var outputParam01 = new Array(inputParam01.length);
            for (var i = 0; i < outputParam01.length; ++i) {
                outputParam01[i] = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();
            }

            var inputParamInt = inputParam01[0].getNamedIntValue("param01");
            outputParam01[0].setNamedIntValue("result", inputParamInt + 1);

            var inputParamLong = inputParam01[1].getNamedInt64Value("param01");
            outputParam01[1].setNamedInt64Value("result", inputParamLong - 1);

            var inputParamFloat = inputParam01[2].getNamedFloatValue("param01");
            outputParam01[2].setNamedFloatValue("result", inputParamFloat * 3);

            echoStrings(inputParam01[3], outputParam01[3]);
            output.setNamedArchiveCollection("resultLevel1", outputParam01);
        } catch (e) {
            return e.number;
        }
    };

    function inputOutputArchiveCollectionToArchiveCollections(input, output) {
        var IN_OUT_ARCHIVES_COUNT = 4;
        try {
            var inputParam01 = input.getNamedArchiveCollection("param01");
            var outputParam01 = new Array(IN_OUT_ARCHIVES_COUNT);

            for (var i = 0; i < outputParam01.length; ++i) {
                outputParam01[i] = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();
            }

            inputOutputIntCollection(inputParam01[0], outputParam01[0]);
            inputOutputInt64Collection(inputParam01[1], outputParam01[1]);
            inputOutputFloatCollection(inputParam01[2], outputParam01[2]);
            inputOutputStringCollection(inputParam01[3], outputParam01[3]);

            output.setNamedArchiveCollection("resultLevel1", outputParam01);

        } catch (e) {
            return e.number
        }
    };

    function inputOutputFloatCollection(input, output) {
        try {
            var inputParam01 = input.getNamedFloatCollection("param01");
            reverseTypedArray(inputParam01);
            output.setNamedFloatCollection("result", inputParam01);
        } catch (e) {
            return e.number;
        }
    };

    function inputOutputIntCollection(input, output) {
        try {
            var inputParam01 = input.getNamedIntCollection("param01");
            reverseTypedArray(inputParam01);
            output.setNamedIntCollection("result", inputParam01);
        } catch (e) {
            return e.number;
        }
    };

    function inputOutputInt64Collection(input, output) {
        try {
            var inputParam01 = input.getNamedInt64Collection("param01");
            reverseTypedArray(inputParam01);
            output.setNamedInt64Collection("result", inputParam01);
        } catch (e) {
            return e.number;
        }
    };

    function inputOutputIntCollectionAndIntNumber(input, output) {
        try {
            var inputParam02 = input.getNamedIntValue("param02");
            var inputParam01 = input.getNamedIntCollection("param01");
            for (var i = 0; i < inputParam01.length; ++i) {
                inputParam01[i] += inputParam02;
            }
            output.setNamedIntCollection("result", inputParam01);
        } catch (e) {
            return e.number;
        }
    };

    function inputOutputMultipleCollections(input, output) {
        try {

            // String Collection
            var stringBuffer1 = input.getNamedStringCollection("param01");
            reverseTypedArray(stringBuffer1);
            output.setNamedStringCollection("result1", stringBuffer1);

            // int Collection
            var inputParam02 = input.getNamedIntCollection("param02");
            reverseTypedArray(inputParam02);
            output.setNamedIntCollection("result2", inputParam02);

            // String Collection
            var stringBuffer3 = input.getNamedStringCollection("param03");
            reverseTypedArray(stringBuffer3);
            output.setNamedStringCollection("result3", stringBuffer3);

            // Float Collection
            var inputParam04 = input.getNamedFloatCollection("param04");
            reverseTypedArray(inputParam04);
            output.setNamedFloatCollection("result4", inputParam04);

        } catch (e) {
            return e.number;
        }

    };

    function inputOutputMultipleCollectionsForRpcArchive(input, output) {
        // Part 1 - which is a string collection
        var stringBuffer1 = input.getNamedStringCollection("param01");
        reverseTypedArray(stringBuffer1);
        output.setNamedStringCollection("result1", stringBuffer1);

        // Part 2 - which is a float collection
        var inputParam02 = input.getNamedFloatCollection("param02");
        reverseTypedArray(inputParam02);
        output.setNamedFloatCollection("result2", inputParam02);

        // Part 3 - which is a int collection
        var inputParam03 = input.getNamedIntCollection("param03");
        reverseTypedArray(inputParam03);
        output.setNamedIntCollection("result3", inputParam03);
    };

    function inputOutputStringCollection(input, output) {
        var stringBuffer = input.getNamedStringCollection("stringCollection");
        reverseTypedArray(stringBuffer);
        output.setNamedStringCollection("result", stringBuffer);
    };

    function inputOutputSubtraction(input, output) {
        try {
            var inputParam1 = input.getNamedShortValue("param01");
            var inputParam2 = input.getNamedShortValue("param02");

            if (inputParam1 != 50 || inputParam2 != 30) {
                return E_INVALIDARG;
            }

            output.setNamedShortValue("result", inputParam1 - inputParam2);
        } catch (e) {
            return e.number;
        }
    };

    function inputPlayerNoOutput(input, output) {
        try{
            var playerArchive = input.getNamedArchiveValue("playerData");
            var p = RPC.CustomTypes.Player.fromRpcArchive(playerArchive);

            return verifyIncomingPlayerData(p);
        } catch (e) {
            return e.number;
        }
    };

    function inputPlayerOutputPlayer(input, output) {
        var hr = inputPlayerNoOutput(input,output);
        if (hr != 0) {
            return hr;
        }

        return noInputPlayerOutput(input, output);
    }

    function inputRpcArchiveToAnotherRpcArchiveCollectionForLoad(input, output) {
        try {
            var rpcArchiveInput = input.getNamedArchiveValue("rpcMultilevel");
            var rpcArchiveOutput = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();

            var level = input.getNamedIntValue("level");

            if (level > 0) {
                for (var i = 1; i < level; ++i) {
                    var rpcArchiveInputNode = rpcArchiveInput.getNamedArchiveValue("rpcNode");
                    rpcArchiveInput = rpcArchiveInputNode;
                }

                var inputParam01 = rpcArchiveInput.getNamedArchiveCollection("rpcCollection");
                var outputParam01 = new Array(inputParam01);
                for (var i = 0; i < outputParam01.length; ++i) {
                    outputParam01[i] = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();
                }

                for (var i = 0; i < inputParam01.length; ++i) {
                    inputOutputMultipleCollectionsForRpcArchive(inputParam01[i], outputParam01[i]);
                }

                rpcArchiveOutput.setNamedArchiveCollection("rpcCollectionResult", outputParam01);

                for (var i = 1; i < level; ++i) {
                    var rpcArchiveOutputTemp = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();
                    rpcArchiveOutputTemp.setNamedArchiveValue("rpcNodeResult", rpcArchiveOutput);
                    rpcArchiveOutput = rpcArchiveOutputTemp;
                }

                output.setNamedArchiveValue("rpcMultilevelResult", rpcArchiveOutput);

            } else {
                return E_FAIL;
            }
        } catch (e) {
            return e.number;
        }
    };

    function inputRpcArchiveToAnotherRpcArchiveForLoad(input, output) {
        try {
            var rpcArchiveInput = input.getNamedArchiveValue("rpcMultilevel");
            var rpcArchiveOutput = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();
            var level = input.getNamedIntValue("level");

            if (level > 0) {
                for (var i = 1; i < level; ++i) {
                    var rpcArchiveInputNode = rpcArchiveInput.getNamedArchiveValue("rpcNode");
                    rpcArchiveInput = rpcArchiveInputNode;
                }

                inputOutputMultipleCollectionsForRpcArchive(rpcArchiveInput, rpcArchiveOutput);

                for (var i = 1; i < level; ++i) {
                    var rpcArchiveOutputTemp = new Microsoft.Internal.GamesTest.Rpc.Server.RpcArchive();
                    rpcArchiveOutputTemp.setNamedArchiveValue("rpcNodeResult", rpcArchiveOutput);
                    rpcArchiveOutput = rpcArchiveOutputTemp;
                }

                output.setNamedArchiveValue("rpcMultilevelResult", rpcArchiveOutput);
            } else {
                E_FAIL;
            }
        } catch (e) {
            return e.number;
        }
    };

    function largeNumberOfInputValues(input, output) {
        try {
            var numParams = input.getNamedIntValue("numParams");

            var sum = 0;
            for (var i = 0; i < numParams; ++i) {
                var fieldName = "in" + ("00" + i).slice(-3);
                var currentValue = input.getNamedIntValue(fieldName);

                sum += currentValue;
            }

            output.setNamedIntValue("result", sum);
        } catch (e) {
            return e.number;
        }
    };

    function multiInputMultiOutput(input, output) {
        try  {
            var inputParam1 = input.getNamedByteValue("param01");
            var inputParam2 = input.getNamedByteValue("param02");
            var inputParam3 = input.getNamedDoubleValue("param03");

            if (inputParam1 != 112 || inputParam2 != 79 || inputParam3 != 93.125) {
                return E_INVALIDARG;
            }

            output.setNamedByteValue("addOneAndTwo", inputParam1 + inputParam2);
            output.setNamedDoubleValue("paramThreeSquared", inputParam3 * inputParam3);
            output.setNamedByteValue("subtractTwoFromOne", inputParam1 - inputParam2);
        } catch (e) {
            return e.number;
        }
    };

    function noInputNoOutput(input, output) {
    };

    function noInputPlayerOutput(input, output) {
        try {
            var p = initializeOutgoingPlayer();
            p.toRpcArchive(output);
        } catch (e) {
            return e.number;
        }
    };

    function outputNoInput(input, output) {
        try {
            output.setNamedIntValue("result", 3871568);
        } catch (e) {
            return e.number;
        }
    };

    function retrieveMissingRpcArchiveField(input, output) {
        try {
            var notUsed = input.getNamedDoubleValue("nonExistentField");
        } catch (e) {
            return ARCHIVE_FIELD_NOT_FOUND;
        }
    };

    function returnErrorCode(input, output) {
        return E_FAIL;
    };

    function returnLargeNumberOfValues(input, output) {
        try {
            var numOutputValues = input.getNamedIntValue("numOutputValues");
            for (var i = 0; i < numOutputValues; ++i) {
                var fieldName = "out" + ("00" + i).slice(-3);
                output.setNamedIntValue(fieldName, i);
            }
        } catch (e) {
            return e.number;
        }
    };

    function rpcArchiveValueIsNull(input, output) {
        return ARCHIVE_VALUE_IS_NULL
    };

    function simultaneousHookCalls(input, output) {
        try {
            var param = input.getNamedWordValue("param");

            if (param != 52 && param != 71) {
                return E_INVALIDARG;
            }

            output.setNamedWordValue("result", param * 100);
        } catch (e) {
            return e.number;
        }
    };

    function stopTheServer(input, output) {
        var result = 0;
        try {
            rpcServer2.stop();
        } catch (e) {
            result = e.number;
        }
        output.setNamedInt64Value("result", result)
    };

    function throwException(input, output) {
        var errorCode = input.getNamedIntValue("errorCode");

        switch (errorCode) {
            case 600:
                return TEST_EXCEPTION_1;
            case 601:
                return TEST_EXCEPTION_2;
            case 602:
                return TEST_EXCEPTION_3;
            default:
                return 0;
        }
    };

    function echoBinary(input, output) {
        try {
        output.setNamedBlobValue("blob", input.getNamedBlobValue("blob"));
        } catch (e) {
            return e.number;
        }
    };

    function startServerWithQueueSize(input, output) {
        try {
            var queueSize = input.getNamedDwordValue("queueSize");
            var serverId = "{456F8F5C-0B90-465B-8D4E-D10CC8D690C0}";

            rpcServerWithQueueSize = new Microsoft.Internal.GamesTest.Rpc.Server.RpcServer(
                serverId,
                Microsoft.Internal.GamesTest.Rpc.Server.RpcServer.defaultPort,
                queueSize);

            rpcServerWithQueueSize.start();
        } catch (e) {
            return e.number;
        }
    }

    function stopServerWithQueueSize(input, output) {
        try {
            if (rpcServerWithQueueSize != null) {
                rpcServerWithQueueSize.stop();
                rpcServerWithQueueSize = null;
            }
        } catch (e) {
            return e.number;
        }
    }

    function runServerWithQueueSize(input, output)
    {
        try {
            if (rpcServerWithQueueSize != null) {
                rpcServerWithQueueSize.processPendingRequestsAsync();
            }
        } catch (e) {
            return e.number;
        }
    }

    function addBinaryDataToArchive(input, output) {
        try {
            // Getting the input RpcBinaryData object
            var param = input.getNamedBlobValue("param01");
		
            var bytes = new Uint8Array(param.data.length);

            var dataReader = Windows.Storage.Streams.DataReader.fromBuffer(param.data);
            dataReader.readBytes(bytes)
            dataReader.close();

            reverseTypedArray(bytes);

            param.data = Windows.Security.Cryptography.CryptographicBuffer.createFromByteArray(bytes);

            // Setting the output of Hook method
            output.setNamedBlobValue("param01", param);
        } catch (e) {
            return e.number;
        }
    };

	
    function addBinaryDataCollectionToArchive(input, output) {
        try {
            var inputParam01 = input.getNamedBlobCollection("param01");
            for(var i = 0; i < inputParam01.length; ++i) {
                var buffer = inputParam01[i].data;

                var bytes = new Uint8Array(buffer.length);
                var dataReader = Windows.Storage.Streams.DataReader.fromBuffer(buffer);
                dataReader.readBytes(bytes);
                dataReader.close();

                reverseTypedArray(bytes);

                inputParam01[i].data = Windows.Security.Cryptography.CryptographicBuffer.createFromByteArray(bytes);
            }

            output.setNamedBlobCollection("result", inputParam01);
        } catch (e) {
            return e.number;
        }
    };

    function noInputBinaryDataOutput(input, output) {
        try {
            var arraySize = 5;

            var data = new Uint8Array(arraySize);
            for (var i = 0; i < arraySize; ++i) {
                data[i] = i;
            }

            var iBuffer = Windows.Security.Cryptography.CryptographicBuffer.createFromByteArray(data);
            var outRpcBinaryData = new Microsoft.Internal.GamesTest.Rpc.Server.RpcBinaryData(iBuffer);
		
            output.setNamedBlobValue("result", outRpcBinaryData);
        } catch (e) {
            return e.number;
        }
    };

    function processPendingRequests() {
        try {
            var hr1 = null;
            var hr2 = null;
            try {
                rpcServer.processPendingRequestsAsync();
            } catch (e) {
                hr1 = e;
            }

            try {
                rpcServer2.processPendingRequestsAsync();
            } catch (e) {
                hr2 = e;
            }

            if (hr1 != null) {
                return hr1;
            }
            else if (hr2 != null) {
                return hr2;
            }
            setTimeout(processPendingRequests, 500);
        } catch (e) {
            RPC.TestHooks.shutdown();
        }
    };

    WinJS.Namespace.define("RPC.TestHooks", {

        triggerEvent: function (eventName, eventParameter) {
            try {
                var stringValue = null;
				
                stringValue = eventName.getNamedStringValue("eventName");
                                         
                eventParameter.setNamedStringValue("result", "Test Event Parameter");

                // This need to be implemented
                rpcServer.raiseEvent(stringValue, eventParameter);

                // raise event for server for test case 12
                // If required we can optionally send server name as parameter in RpcArchive object to avoid both raise events to be called
                rpcServer3.raiseEvent(stringValue, eventParameter);
            } catch (e) {
                return e.number;
            }
        },   
        

        startProcessingRequests: function () {
            processPendingRequests();
        },
        initialize: function (port) {
            var content = document.getElementById("testContent");
            
            var portStuff = "Initializing with port: " + port;
            rpcServer = new Microsoft.Internal.GamesTest.Rpc.Server.RpcServer(Microsoft.Internal.GamesTest.Rpc.Server.RpcServer.defaultServerId, port == 0 ? Microsoft.Internal.GamesTest.Rpc.Server.RpcServer.defaultPort : port);

            portStuff += "\r\n Initialized with port: " + rpcServer.port;

            content.textContent = portStuff

            rpcServer.registerHookMethodAsync("OutputNoInput", outputNoInput);
            rpcServer.registerHookMethodAsync("InputNoOutput", inputNoOutput);
            rpcServer.registerHookMethodAsync("InputOutput", inputOutput);
            rpcServer.registerHookMethodAsync("NoInputNoOutput", noInputNoOutput);
            rpcServer.registerHookMethodAsync("MultiInputMultiOutput", multiInputMultiOutput);
            rpcServer.registerHookMethodAsync("HookTimesOut", hookTimesOut);
            rpcServer.registerHookMethodAsync("ReturnLargeNumberOfValues", returnLargeNumberOfValues);
            rpcServer.registerHookMethodAsync("ReturnErrorCode", returnErrorCode);
            rpcServer.registerHookMethodAsync("SimultaneousHookCalls", simultaneousHookCalls);
            rpcServer.registerHookMethodAsync("LargeNumberOfInputValues", largeNumberOfInputValues);
            rpcServer.registerHookMethodAsync("EchoStrings", echoStrings);
            rpcServer.registerHookMethodAsync("RetrieveMissingRpcArchiveField", retrieveMissingRpcArchiveField);
            rpcServer.registerHookMethodAsync("RpcArchiveValueIsNull", rpcArchiveValueIsNull);
            rpcServer.registerHookMethodAsync("CallProcessPendingRequestsOnceOnServer3", callProcessPendingRequestsOnceOnServer3);
            rpcServer.registerHookMethodAsync("InputOutputIntCollection", inputOutputIntCollection);
            rpcServer.registerHookMethodAsync("InputOutputInt64Collection", inputOutputInt64Collection);
            rpcServer.registerHookMethodAsync("InputOutputFloatCollection", inputOutputFloatCollection);
            rpcServer.registerHookMethodAsync("InputOutputStringCollection", inputOutputStringCollection);
            rpcServer.registerHookMethodAsync("InputOutputIntCollectionAndIntNumber", inputOutputIntCollectionAndIntNumber);
            rpcServer.registerHookMethodAsync("InputOutputMultipleCollections", inputOutputMultipleCollections);
            rpcServer.registerHookMethodAsync("TriggerEvent", RPC.TestHooks.triggerEvent);
            rpcServer.registerHookMethodAsync("AddArchiveToArchive", addArchiveToArchive);
            rpcServer.registerHookMethodAsync("AddArchiveToArchiveWithIntColl", addArchiveToArchiveWithIntColl);
            rpcServer.registerHookMethodAsync("AddArchiveToArchiveWithStringColl", addArchiveToArchiveWithStringColl);
            rpcServer.registerHookMethodAsync("AddArchiveToArchiveWithInt64Coll", addArchiveToArchiveWithInt64Coll);
            rpcServer.registerHookMethodAsync("InputOutputArchiveCollectionToArchive", inputOutputArchiveCollectionToArchive);
            rpcServer.registerHookMethodAsync("InputOutputArchiveCollectionToArchiveCollections", inputOutputArchiveCollectionToArchiveCollections);
            rpcServer.registerHookMethodAsync("InputRpcArchiveToAnotherRpcArchiveForLoad", inputRpcArchiveToAnotherRpcArchiveForLoad);
            rpcServer.registerHookMethodAsync("InputRpcArchiveToAnotherRpcArchiveCollectionForLoad", inputRpcArchiveToAnotherRpcArchiveCollectionForLoad);
            rpcServer.registerHookMethodAsync("InputPlayerNoOutput", inputPlayerNoOutput);
            rpcServer.registerHookMethodAsync("NoInputPlayerOutput", noInputPlayerOutput);
            rpcServer.registerHookMethodAsync("InputPlayerOutputPlayer", inputPlayerOutputPlayer);
            rpcServer.registerHookMethodAsync("ThrowException", throwException);
            rpcServer.registerHookMethodAsync("EchoBinary", echoBinary);
            rpcServer.registerHookMethodAsync("AddBinaryDataToArchive", addBinaryDataToArchive);
            rpcServer.registerHookMethodAsync("AddBinaryDataCollectionToArchive", addBinaryDataCollectionToArchive);
            rpcServer.registerHookMethodAsync("NoInputBinaryDataOutput", noInputBinaryDataOutput);
            rpcServer.registerHookMethodAsync("StartServerWithQueueSize", startServerWithQueueSize);
            rpcServer.registerHookMethodAsync("StopServerWithQueueSize", stopServerWithQueueSize);
            rpcServer.registerHookMethodAsync("RunServerWithQueueSize", runServerWithQueueSize);

            try
            {
                rpcServer.start();
            }
            catch(e)
            {
                Debug.writeln("Failed to start the first server.");
                throw e;
            }

            // Warning: If you modify this guid then you need to modify the same guid value in Rpc.Tests.XboxEndToEndTests.cs file.
            // {4FC5C96E-2ECD-4A45-BF88-59EFFC184682}
            var guid = "{4FC5C96E-2ECD-4A45-BF88-59EFFC184682}";

            rpcServer2 = new Microsoft.Internal.GamesTest.Rpc.Server.RpcServer(guid, Microsoft.Internal.GamesTest.Rpc.Server.RpcServer.defaultPort);
            rpcServer2.registerHookMethodAsync("InputOutputSubtraction", inputOutputSubtraction);
            rpcServer2.registerHookMethodAsync("StopTheServer", stopTheServer);
            rpcServer2.registerHookMethodAsync("CallProcessPendingRequestsOnceOnServer3", callProcessPendingRequestsOnceOnServer3);
            try
            {
                rpcServer2.start();
            }
            catch(e)
            {
                Debug.writeln("Failed to start the second server.");
                throw e;
            }

            // {48810C25-BC83-4934-9085-8B00275FD4F5}
            var guid3 = "{48810C25-BC83-4934-9085-8B00275FD4F5}";
            

            rpcServer3 = new Microsoft.Internal.GamesTest.Rpc.Server.RpcServer(guid3, Microsoft.Internal.GamesTest.Rpc.Server.RpcServer.defaultPort);
            rpcServer3.registerHookMethodAsync("InputOutput", inputOutput);
            rpcServer3.registerHookMethodAsync("InputOutputImmediate", inputOutput, true);
            rpcServer3.registerHookMethodAsync("InputNoOutput", inputNoOutput);
            rpcServer3.registerHookMethodAsync("OutputNoInput", outputNoInput);
            rpcServer3.registerHookMethodAsync("MultiInputMultiOutput", multiInputMultiOutput);
            rpcServer3.registerHookMethodAsync("HookOrderIncrementNoOutput", hookOrderIncrementNoOutput);
            rpcServer3.registerHookMethodAsync("GetHookOrderCounter", getHookOrderCounter);
            rpcServer3.registerHookMethodAsync("TriggerEvent", RPC.TestHooks.triggerEvent);

            try
            {
                rpcServer3.start();
            }
            catch(e)
            {
                Debug.writeln("Failed to start the third server.");
                throw e;
            }
        },
        shutdown: function () {
            try {
                rpcServer.stop();
            } catch (e) {
                Debug.writeln(e.toString());
            }
            try {
                rpcServer2.stop();
            } catch (e) {
                Debug.writeln(e.toString());
            }
            try {
                rpcServer3.stop();
            } catch (e) {
                Debug.writeln(e.toString());
            }

        }
    });
})();